package com.filetransfer.api.upload;
import com.filetransfer.utils.MD5;

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;

import cn.hutool.core.lang.func.Func0;
import lombok.Getter;
@Getter
public class FileChunkPacker {
    public FileChunkPacker(String fileName, Func0<InputStream> getFile, int chunkSize) {
        this.getFile = getFile;
        this.file = null;
        this.chunkSize = chunkSize;
        this.fileName = fileName;
        //整个文件的MD5
        try (var fs = getFile.call()) {
            this.totalSize = fs.available();
            fileMd5 = MD5.calcFile(fs);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    public FileChunkPacker(File file, int chunkSize) {
        this.file = file;
        this.totalSize = file.length();
        this.fileName = file.getName();
        this.getFile = null;
        this.chunkSize = chunkSize;
        //整个文件的MD5
        fileMd5 = MD5.calcFile(file);
    }
    // --- 属性 ---
    private final int chunkSize;
    private final File file;
    private final Func0<InputStream> getFile;
    private final long totalSize;
    private final String fileName;
    @Getter
    private final String fileMd5;
    //获取分片数量
    public long getChunkCount() {
        return (getTotalSize() + chunkSize - 1) / chunkSize;
    }
    /**
     * @noinspection resource
     */ //准备分片
    public CloseableIterator<FileChunk> prepare() throws FileNotFoundException {
        //读入文件
        final InputStream fs;
        try {
            if (file == null) {
                fs = getFile.call();
            } else {
                fs = new FileInputStream(file);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        //分片
        return new CloseableIterator<>() {
            //当前分片索引
            private int chunkIndex = 0;
            //剩余字节数
            private long remainingLength = getTotalSize();
            private long offset = 0;
            //是否有下一个分片
            @Override
            public boolean hasNext() {
                return remainingLength > 0;
            }
            //获取下一个分片
            @Override
            public FileChunk next() {
                try {
                    //计算当前分片大小
                    var currentChunkSize = (int) Math.min(chunkSize, remainingLength);
                    //读取当前分片数据
                    var chunkData = new byte[currentChunkSize];
                    //仅读取当前分片大小的数据，不读取整个文件，避免内存溢出
                    long currentOffset = offset;
                    fs.read(chunkData, 0, currentChunkSize);
                    offset += currentChunkSize;
                    //计算当前分片MD5
                    var chunkMD5 = MD5.calcBytes(chunkData);
                    //更新剩余字节数
                    remainingLength -= currentChunkSize;
                    //返回分片
                    return new FileChunk(chunkIndex++, currentOffset, chunkData, chunkMD5);
                } catch (Exception e) {
                    //爆了
                    throw new RuntimeException(e);
                }
            }
            @Override
            public void close() throws IOException {
                fs.close();
            }
        };
    }
    public interface CloseableIterator<T> extends Iterator<T>, Closeable {}
}
